package me.dengfengdecao.demo.config.shiro;

import org.apache.shiro.cache.AbstractCacheManager;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

/**
 * Shiro-Redis 缓存管理器
 * Created by dengfengdecao on 16/10/6.
 */
@Component
public class ShiroRedisCacheManager extends AbstractCacheManager implements CacheManager {

    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    protected Cache createCache(String name) throws CacheException {
        return new ShiroRedisCache<Object, Object>(redisTemplate, name);
    }

    /**
     * Shiro-Redis 缓存便利类
     * @param <K>
     * @param <V>
     */
    private static class ShiroRedisCache<K, V> implements Cache<K, V> {

        public static final Logger log = LoggerFactory.getLogger(ShiroRedisCache.class);

        private RedisTemplate<K, V> redisTemplate;
        private String prefix;

        public ShiroRedisCache(RedisTemplate<K, V> redisTemplate) {
            this(redisTemplate, "");
        }

        public ShiroRedisCache(RedisTemplate<K, V> redisTemplate, String prefix) {
            this.redisTemplate = redisTemplate;
            this.prefix = prefix;
        }

        @Override
        public V get(K key) throws CacheException {
            try {
                if (log.isTraceEnabled()) {
                    log.trace("Getting object from cache [" + this.getClass().getSimpleName() + "] for key [" + key + "]");
                }

                if (key == null) {
                    return null;
                } else {
                    V v = redisTemplate.opsForValue().get(key);
                    return v;
                }
            } catch (Throwable var3) {
                throw new CacheException(var3);
            }
        }

        @Override
        public V put(K key, V value) throws CacheException {
            if (log.isTraceEnabled()) {
                log.trace("Putting object in cache [" + this.getClass().getSimpleName() + "] for key [" + key + "]");
            }

            try {
                if (key == null || value == null) {
                    return null;
                }

                if (value instanceof List) {
                    redisTemplate.opsForList().leftPush(key, value);
                } else if (value instanceof Set) {
                    redisTemplate.opsForSet().add(key, value);
                } else {
                    redisTemplate.opsForValue().set(key, value);
                }
                return value;
            } catch (Throwable var5) {
                throw new CacheException(var5);
            }
        }

        @Override
        public V remove(K key) throws CacheException {
            if (log.isTraceEnabled()) {
                log.trace("Removing object from cache [" + this.getClass().getSimpleName() + "] for key [" + key + "]");
            }

            try {
                V t = this.get(key);
                redisTemplate.delete(key);
                return t;
            } catch (Throwable var3) {
                throw new CacheException(var3);
            }
        }

        @Override
        public void clear() throws CacheException {
            if (log.isTraceEnabled()) {
                log.trace("Clearing all objects from cache [" + this.getClass().getSimpleName() + "]");
            }

            try {
                redisTemplate.getConnectionFactory().getConnection().flushDb();
            } catch (Throwable var2) {
                throw new CacheException(var2);
            }
        }

        @Override
        public int size() {
            try {
                Long size = redisTemplate.getConnectionFactory().getConnection().dbSize();
                return size.intValue();
            } catch (Throwable var2) {
                throw new CacheException(var2);
            }
        }

        @Override
        public Set<K> keys() {
            try {
                String pattern = prefix + "*";
                Set<K> keys = redisTemplate.keys((K) pattern);
                if (CollectionUtils.isEmpty(keys)) {
                    return Collections.emptySet();
                }

                return Collections.unmodifiableSet(keys);
            } catch (Throwable var2) {
                throw new CacheException(var2);
            }
        }

        @Override
        public Collection<V> values() {
            try {
                Set<K> keys = keys();
                if (!CollectionUtils.isEmpty(keys)) {
                    ArrayList<V> values = new ArrayList<V>(keys.size());
                    Iterator<K> i$ = keys.iterator();

                    while (i$.hasNext()) {
                        K key = i$.next();
                        V value = this.get(key);
                        if (value != null) {
                            values.add(value);
                        }
                    }

                    return Collections.unmodifiableList(values);
                } else {
                    return Collections.emptyList();
                }
            } catch (Throwable var6) {
                throw new CacheException(var6);
            }
        }


        public String toString() {
            return "RedisCache [" + this.getClass().getSimpleName() + "]";
        }
    }
}
